import TP2.Benchmark;
import TP2.SorterProviderImpl;
import TP2.dataGen.IntegerDataSetGenerator;
import TP2.ejercicio3.NombreCompleto;
import TP3.ejercicio2.ShellSorter;
import anaydis.sort.Sorter;
import anaydis.sort.SorterType;
import anaydis.sort.data.DataSetGenerator;
import anaydis.sort.data.StringDataSetGenerator;
import anaydis.sort.gui.ObservableSorter;
import anaydis.sort.provider.SorterProvider;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Testing {
    Scanner sc;
    SorterProvider sorterProvider;

    public static void main(String[] Args){
        Testing a = new Testing();
        a.menu();
    }

    public Testing(){
        sc = new Scanner(System.in);
        sorterProvider = new SorterProviderImpl();
    }

    private void menu(){
        System.out.println("1. Name Sort Example");
        System.out.println("2. Elemental Sorters Test");
        System.out.println("3. Shellsort Sequence Test");
        //System.out.println("4. Best Quicksort M Test");
        //System.out.println("5. Quicksort Variant Test");
        //System.out.println("6. Mergesort Test");
        //System.out.println("7. Heapsort Test");
        //System.out.println("8. Moby Dick Indexing Test ");
        System.out.println("Any other key: Exit");
        System.out.println(" ");
        switch(sc.nextInt()){
            case 1:
                nameSortExample();
                break;
            case 2:
                elementalTest();
                break;
            case 3:
                shellTest();
                break;
            case 4:
                quicksortMTest();
                break;
            case 5:
                quicksortVariantTest();
                break;
            case 6:
                mergesortTest();
                break;
            case 7:
                heapsortTest();
                break;
            case 8:
                wordsWordsTest();
                break;
            default:
                System.exit(-1);
        }
    }

    private void quicksortMTest(){

    }

    private void quicksortVariantTest(){
    /*    System.out.println("Testing the three elemental sorters:");
        IntegerDataSetGenerator intGen = new IntegerDataSetGenerator();
        List<Sorter> sorters = new ArrayList<Sorter>();
        int[] n = {10, 50, 100, 250, 500, 1000, 2000, 3000, 4000, 5000};
        Sorter nonRecursive = (QuickSorter) new sorterProvider.getSorterForType(SorterType.QUICK);
        nonRecursive.setImpl(0);
        sorters.add(nonRecursive);
        System.out.println(" ");
        for(int i : n){
            printAscendingTest(intGen, sorters, i);
            System.out.println(" ");
            printDescendingTest(intGen, sorters, i);
            System.out.println(" ");
            printRandomTest(intGen, sorters, i);
            System.out.println(" ");
        }
        System.out.println("Testing finished. Returning to menu.");
        System.out.println(" ");
        System.out.println(" ");
        menu();                                                                                                                 */
    } //Hay que cambiar la forma en que está implementado el quick porque no puedo hacer esto para cambiar la implementación...

    private void mergesortTest(){
        System.out.println("Testing Mergesort:");
        IntegerDataSetGenerator intGen = new IntegerDataSetGenerator();
        List<Sorter> sorters = new ArrayList<Sorter>();
        sorters.add(sorterProvider.getSorterForType(SorterType.MERGE));
        int[] n = {10, 50, 100, 250, 500, 1000, 2000, 3000, 4000, 5000};
        System.out.println(" ");
        for(int i : n){
            printAscendingTest(intGen, sorters, i);
            System.out.println(" ");
            printDescendingTest(intGen, sorters, i);
            System.out.println(" ");
            printRandomTest(intGen, sorters, i);
            System.out.println(" ");
        }
        System.out.println("Testing finished. Returning to menu.");
        System.out.println(" ");
        System.out.println(" ");
        menu();
    }

    private void heapsortTest(){
        System.out.println("Testing Heapsort:");
        IntegerDataSetGenerator intGen = new IntegerDataSetGenerator();
        List<Sorter> sorters = new ArrayList<Sorter>();
        sorters.add(sorterProvider.getSorterForType(SorterType.HEAP));
        int[] n = {10, 50, 100, 250, 500, 1000, 2000, 3000, 4000, 5000};
        System.out.println(" ");
        for(int i : n){
            printAscendingTest(intGen, sorters, i);
            System.out.println(" ");
            printDescendingTest(intGen, sorters, i);
            System.out.println(" ");
            printRandomTest(intGen, sorters, i);
            System.out.println(" ");
        }
        System.out.println("Testing finished. Returning to menu.");
        System.out.println(" ");
        System.out.println(" ");
        menu();
    }

    private void wordsWordsTest(){
        System.out.println("Moby Dick Test:");
        System.out.println("");
        IntegerDataSetGenerator intGen = new IntegerDataSetGenerator();
        List<Sorter> sorters = new ArrayList<Sorter>();
        SorterProviderImpl sorterProvider = new SorterProviderImpl();
        try {
            for (int n = 10; n < 2000; n = n * 5) {
            System.out.println("First "+n+" words:");
            for (Sorter sorter : sorters) {
                List list=new ArrayList();
                File f = new File( "src\\moby_dick.txt" );
                BufferedReader input = new BufferedReader( new FileReader(f));
                for(int i=0;i<n;i++){
                    if ( f.exists() ) {
                        list.add((input.read()));
                    }
                }
                Benchmark benchMark = new Benchmark();
                ((ObservableSorter) sorter).addSorterListener(benchMark);
                long firstTime = System.currentTimeMillis();
                sorter.sort(intGen.getComparator(), list);
                long lastTime = System.currentTimeMillis();
                System.out.println("Method "+ sorter.getType().toString()+ " had the following output: /n Time: " + (lastTime - firstTime) + " Millisec, " +
                        +benchMark.getSwaps() + " Swaps ," + benchMark.getComparisons() + " Comparisons");
            }
            System.out.println("");
        }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void nameSortExample(){
        System.out.println("Select number of people (less than 118):");
        int i = sc.nextInt();
        StringDataSetGenerator stringGen = new StringDataSetGenerator();
        List names = stringGen.createRandom(i);
        List surnames = stringGen.createRandom(i);
        List<NombreCompleto> people = new ArrayList<NombreCompleto>();
        for(int j=0; j<i; j++){
            people.add(new NombreCompleto((String) names.get(j), (String) surnames.get(j)));
        }
        System.out.println("Listing people in 'Surname, Name' format:");
        /*for(int j=0; j<people.size(); j++){
            System.out.println(people.get(j).toString());
        } */
        List<String> surnameName = new ArrayList<String>();
        List<String> nameSurname = new ArrayList<String>();
        for(NombreCompleto n : people){
            surnameName.add(n.toStringSurnameFirst());
        }
        for(NombreCompleto n : people){
            nameSurname.add(n.toStringNameFirst());
        }
        for(String n : surnameName){
            System.out.println(n);
        }
        System.out.println(" ");
        System.out.println("Prioritizing stability, the list is insertion-sorted.");
        System.out.println("(The way in which the lists are generated doesn't allow repeated keys anyways)");
        Sorter insSorter = sorterProvider.getSorterForType(SorterType.INSERTION);
        insSorter.sort(stringGen.getComparator(), surnameName);
        insSorter.sort(stringGen.getComparator(), nameSurname);
        System.out.println(" ");
        System.out.println("Sorted by name:");
        for(String n : nameSurname){
            System.out.println(n);
        }
        System.out.println(" ");
        System.out.println("Sorted by surname:");
        for(String n : surnameName){
            System.out.println(n);
        }
        System.out.println(" ");
        System.out.println("End of Name Sort Example. Returning to menu.");
        System.out.println(" ");
        System.out.println(" ");
        menu();
    }

    private void elementalTest(){
        System.out.println("Testing the three elemental sorters:");
        IntegerDataSetGenerator intGen = new IntegerDataSetGenerator();
        List<Sorter> sorters = new ArrayList<Sorter>();
        sorters.add(sorterProvider.getSorterForType(SorterType.INSERTION));
        sorters.add(sorterProvider.getSorterForType(SorterType.BUBBLE));
        sorters.add(sorterProvider.getSorterForType(SorterType.SELECTION));
        int[] n = {10, 50, 100, 250, 500, 1000, 2000, 3000, 4000, 5000};
        System.out.println(" ");
        for(int i : n){
            printAscendingTest(intGen, sorters, i);
            System.out.println(" ");
            printDescendingTest(intGen, sorters, i);
            System.out.println(" ");
            printRandomTest(intGen, sorters, i);
            System.out.println(" ");
        }
        System.out.println("Testing finished. Returning to menu.");
        System.out.println(" ");
        System.out.println(" ");
        menu();
    }

    private void shellTest(){
        System.out.println("Testing Shellsort sequences:");
        int[] n = {100, 1000, 10000, 50000};
        IntegerDataSetGenerator intGen = new IntegerDataSetGenerator();
        List<Sorter> sorters = new ArrayList<Sorter>();
        ShellSorter shellSorter = (ShellSorter) sorterProvider.getSorterForType(SorterType.SHELL);
        sorters.add(shellSorter);
        System.out.println(" ");
        for(int i : n){
            System.out.println("Sequence: 1, 4, 13, 40, 121...");
            shellSorter.setSequence(buildKnuthSequence(i));
            printAscendingTest(intGen, sorters, i);
            System.out.println(" ");
            printDescendingTest(intGen, sorters, i);
            System.out.println(" ");
            printRandomTest(intGen, sorters, i);
            System.out.println(" ");
            System.out.println(" ");
            System.out.println("Sequence: 1, 8, 23, 77, 281...");
            shellSorter.setSequence(buildSecondSequence(i));
            printAscendingTest(intGen, sorters, i);
            System.out.println(" ");
            printDescendingTest(intGen, sorters, i);
            System.out.println(" ");
            printRandomTest(intGen, sorters, i);
        }
        System.out.println("Testing finished. Returning to menu.");
        System.out.println(" ");
        System.out.println(" ");
        menu();
    }

    private Integer[] buildKnuthSequence(int length){
        List<Integer> sequence = new ArrayList<Integer>();
        for(int i=0; i<length; i = i*3 +1 ){
            sequence.add(i);
        }
        Integer[] seq = new Integer[sequence.size()];
        for(int i=0; i<sequence.size(); i++){
            seq[i] = sequence.get(i);
        }
        return seq;
    }

    private Integer[] buildSecondSequence(int length){
        List<Integer> sequence = new ArrayList<Integer>();
        sequence.add(1);
        for(int i=0; i<length; i =(int) (Math.pow(4, i+1)+3*Math.pow(2, i)+1)){
            sequence.add(i);
        }
        Integer[] seq = new Integer[sequence.size()];
        for(int i=0; i<sequence.size(); i++){
            seq[i] = sequence.get(i);
        }
        return seq;
    }

    private void printRandomTest(DataSetGenerator dataGen, Iterable<Sorter> sorters, int i){
        System.out.println("Random list with "+i+" elements:");
        List<Integer> list = dataGen.createRandom(i);
        ArrayList aux = new ArrayList(list);
        for (Sorter sorter : sorters) {
            sortAndPrint(dataGen, sorter, aux);
            aux = new ArrayList(list);
        }
    }

    private void printDescendingTest(DataSetGenerator dataGen, Iterable<Sorter> sorters, int i){
        System.out.println("Descending list with "+i+" elements:");
        List<Integer> list = dataGen.createDescending(i);
        ArrayList aux = new ArrayList(list);
        for (Sorter sorter : sorters) {
            sortAndPrint(dataGen, sorter, aux);    //Even in your sort tests, A.U.X.
            aux = new ArrayList(list);
        }
    }

    private void printAscendingTest(DataSetGenerator dataGen, Iterable<Sorter> sorters, int i){
        System.out.println("Ascending list with "+i+" elements:");
        List<Integer> list = dataGen.createAscending(i);
        ArrayList aux = new ArrayList(list);
        for (Sorter sorter : sorters) {
            sortAndPrint(dataGen, sorter, aux);
            aux = new ArrayList(list);
        }
    }

    private void sortAndPrint(DataSetGenerator dataGen, Sorter sorter, List<Integer> list){
        Benchmark benchmark = new Benchmark();
        ((ObservableSorter) sorter).addSorterListener(benchmark);
        long startNano = System.nanoTime();
        long startMili = System.currentTimeMillis();
        sorter.sort(dataGen.getComparator(), list);
        long endNano = System.nanoTime();
        long endMili = System.currentTimeMillis();
        System.out.println("-- "+sorter.getType().toString()+" --");
        System.out.println("Time in milliseconds:" + (endMili - startMili));
        System.out.println("Time in nanoseconds:" + (endNano - startNano));
        System.out.println("Swaps:" + benchmark.getSwaps());
        System.out.println("Comparisons:" + benchmark.getComparisons());
    }

}
